package com.example.springboottest.example.thread;

import org.jetbrains.annotations.NotNull;

import java.util.ArrayDeque;
import java.util.Queue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ThreadWait2Test<E> {
    private Queue<E> queue = null;
    private int limit;
    private Lock lock = new ReentrantLock();
    private Condition notFull = lock.newCondition();
    private Condition notEmpty = lock.newCondition();

    public ThreadWait2Test(int limit) {
        this.limit = limit;
        queue = new ArrayDeque<>(limit);
    }

    public void put(E e) throws InterruptedException {
        System.out.println(queue.toString());
        lock.lockInterruptibly();
        try {
            while (queue.size() == limit) {
                notFull.await();
            }
            queue.add(e);
            notEmpty.signal();
        } finally {
            lock.unlock();
        }
    }

    public E take() throws InterruptedException {
        lock.lockInterruptibly();
        try {
            while (queue.isEmpty()) {
                notEmpty.await();
            }
            E e = queue.poll();
            notFull.signal();
            return e;
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        ThreadWait2Test<Integer> threadWaitTest = new ThreadWait2Test<Integer>(5);

        for (int i = 0; i < 10; i++) {
            Push2Thread pushThread = new Push2Thread("pushThread" + i, threadWaitTest);
            pushThread.start();
        }

        for (int i = 0; i < 5; i++) {
            Poll2Thread pollThread = new Poll2Thread("pollThread" + i, threadWaitTest);
            pollThread.start();
        }

        System.out.println("Main Stop");
    }
}

class Push2Thread extends Thread {
    private ThreadWait2Test threadWaitTest;

    public Push2Thread(@NotNull String name, ThreadWait2Test threadWaitTest) {
        super(name);
        this.threadWaitTest = threadWaitTest;
    }

    @Override
    public void run() {
        try {
            int num = 1;
            while (true) {
                System.out.println(Thread.currentThread().getName() + " start");
                this.threadWaitTest.put(Thread.currentThread().getName() + "-" + (num++));
                TimeUnit.SECONDS.sleep((int) (Math.random() * 100));
                System.out.println(Thread.currentThread().getName() + " stop");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class Poll2Thread extends Thread {
    private ThreadWait2Test threadWaitTest;

    public Poll2Thread(@NotNull String name, ThreadWait2Test threadWaitTest) {
        super(name);
        this.threadWaitTest = threadWaitTest;
    }

    @Override
    public void run() {
        try {
            while (true) {
                System.out.println(Thread.currentThread().getName() + " start");
                this.threadWaitTest.take();
                TimeUnit.SECONDS.sleep((int) (Math.random() * 100));
                System.out.println(Thread.currentThread().getName() + " stop");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}